package com.xiaoyu.base;

import android.content.Context;
import android.text.TextUtils;

import androidx.annotation.NonNull;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import androidx.room.migration.Migration;
import androidx.sqlite.db.SupportSQLiteDatabase;

import com.xiaoyu.base.dao.ConversationDao;
import com.xiaoyu.base.dao.MessageDao;
import com.xiaoyu.base.dao.StickerDao;
import com.xiaoyu.base.dao.StickerPackageDao;
import com.xiaoyu.base.dao.UserDao;
import com.xiaoyu.base.dao.UserKvDao;
import com.xiaoyu.base.entity.ConversationEntity;
import com.xiaoyu.base.entity.MessageEntity;
import com.xiaoyu.base.entity.StickerEntity;
import com.xiaoyu.base.entity.StickerPackage;
import com.xiaoyu.base.entity.UserEntity;
import com.xiaoyu.base.entity.UserKvEntity;

import in.srain.cube.util.CLog;

@Database(entities = {StickerEntity.class, StickerPackage.class, MessageEntity.class,
        UserEntity.class, ConversationEntity.class, UserKvEntity.class}, version = 2)
public abstract class AppDatabase extends RoomDatabase {

    private static final String TAG = "AppDatabase";
    private static final String DATABASE_NAME_PREFIX = "app-db-";

    private static DatabaseEntry sCurrentEntry;

    public abstract StickerPackageDao stickerPackageDao();

    public abstract MessageDao messageDao();


    public abstract StickerDao stickerDao();

    public abstract UserDao userDao();

    public abstract UserKvDao userKvDao();


    public abstract ConversationDao conversationDao();

    /**
     * @throws IllegalStateException if database not created
     */
    @NonNull
    public static AppDatabase getCurrentDatabase() {
        final DatabaseEntry entry = sCurrentEntry;
        if (entry == null) {
            throw new IllegalStateException("database not created");
        }
        return entry.database;
    }

    /**
     * @throws IllegalStateException if database not created or is not current uid
     */
    @NonNull
    public static AppDatabase getDatabaseIfCurrentId(String uid) {
        final DatabaseEntry entry = sCurrentEntry;
        if (entry == null) {
            throw new IllegalStateException("database not created");
        }
        if (!entry.isSameUid(uid)) {
            throw new IllegalStateException(String.format("current uid is %s but request %s", entry.uid, uid));
        }
        return entry.database;
    }

    /**
     * @throws IllegalArgumentException if uid is empty
     */
    public static void openDatabase(@NonNull Context context, @NonNull String uid) {
        if (context == null) {
            throw new IllegalArgumentException("context is null");
        }
        if (TextUtils.isEmpty(uid)) {
            throw new IllegalArgumentException("uid is empty");
        }

        synchronized (AppDatabase.class) {
            if (sCurrentEntry != null && !sCurrentEntry.isSameUid(uid)) {
                CLog.w(TAG, "close database cause not current uid %s %s", sCurrentEntry.uid, uid);
                closeDatabase(sCurrentEntry.uid);
            }

            if (sCurrentEntry == null) {
                sCurrentEntry = DatabaseEntry.create(context, uid);
                CLog.i(TAG, "database created %s", uid);
            }
        }
    }

    public static void closeDatabase(String uid) {
        synchronized (AppDatabase.class) {
            if (sCurrentEntry != null && sCurrentEntry.isSameUid(uid)) {
                sCurrentEntry.closeDatabase();
                sCurrentEntry = null;
                CLog.i(TAG, "database closed %s", uid);
            }
        }
    }

    private static String buildDatabaseName(String uid) {
        return DATABASE_NAME_PREFIX + uid;
    }

    private static class DatabaseEntry {

        private final AppDatabase database;
        private final String uid;

        private static DatabaseEntry create(@NonNull Context context, @NonNull String uid) {
            String name = buildDatabaseName(uid);
            AppDatabase database = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, name)
                    .addMigrations(MIGRATION_1_2)
                    .build();
            return new DatabaseEntry(database, uid);
        }

        private DatabaseEntry(@NonNull AppDatabase database, @NonNull String uid) {
            this.database = database;
            this.uid = uid;
        }

        private void closeDatabase() {
            database.close();
        }

        private boolean isSameUid(String uid) {
            return TextUtils.equals(this.uid, uid);
        }
    }

    private static final Migration MIGRATION_1_2 = new Migration(1, 2) {
        @Override
        public void migrate(SupportSQLiteDatabase database) {
            database.execSQL("ALTER TABLE `conversation` ADD COLUMN `topWeight` INTEGER NOT NULL DEFAULT 0");
            database.execSQL("ALTER TABLE `message` ADD COLUMN `invisibleLastContent` INTEGER NOT NULL DEFAULT 0");
        }
    };
}
